package pl.edu.mimuw.irs.core.model.design.net;

import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import pl.edu.mimuw.irs.core.exception.InternalException;
import pl.edu.mimuw.irs.core.exception.InternalException.Cause;
import pl.edu.mimuw.irs.core.model.design.exception.WorkflowStructureException;
import pl.edu.mimuw.irs.core.model.design.resource.Requirement;
import pl.edu.mimuw.irs.core.model.design.resource.Role;
import pl.edu.mimuw.irs.core.model.design.resource.Roles;
import pl.edu.mimuw.irs.core.model.petrinet.INode;
import pl.edu.mimuw.irs.core.model.petrinet.IPlace;
import pl.edu.mimuw.irs.core.model.petrinet.ITransition;
import pl.edu.mimuw.irs.core.model.petrinet.workflow.Workflow;
import pl.edu.mimuw.irs.core.util.Converter;


/**
 * @author Jakub Rauch
 * Created on: 2009-05-16
 */
public class IRSWorkflow extends Workflow implements Serializable {

	private static final long serialVersionUID = 2579015617442316214L;
	
	// *** Attributes
	
	private IRSPlace rootPlace;
	private Roles roles;
	public IRSPlace getRootPlace() {
		return rootPlace;
	}
	public void setRootPlace(IRSPlace rootPlace) {
		this.rootPlace = rootPlace;
		this.addPlace(rootPlace);
	}
	public Roles getRoles() {
		if (roles == null) roles = new Roles();
		return roles;
	}
	public void setRoles(Roles roles) {
		this.roles = roles;
	}
	public Role getRole(String name) {
		return getRoles().get(name);
	}
	public Set<IRSRequirement> getRequirements() {
		Set<IRSRequirement> requirements = new LinkedHashSet<IRSRequirement>();
		for (IPlace place : getPlaces()) {
			if (!(place instanceof IRSRequirement)) continue;
			requirements.add((IRSRequirement) place);
		}
		return requirements;
	}
	public Set<IRSActivity> getActivities() {
		Set<IRSActivity> activities = new LinkedHashSet<IRSActivity>();
		for (ITransition transition : getTransitions()) {
			if (!(transition instanceof IRSActivity)) continue;
			activities.add((IRSActivity) transition);
		}
		return activities;
	}
	/** Returns all activities as {@link #getActivities()} method, but they are represented in
	 * DFS-ordered LinkedList
	 * @return DFS list of activities
	 */
	public List<IRSActivity> getActivitiesDFS() {
		List<IRSActivity> activities = new LinkedList<IRSActivity>();
		findActivitiesDFS(getRootPlace(), activities);
		return activities;
	}
	private void findActivitiesDFS(IRSNode node, List<IRSActivity> list) {
		if (node instanceof IRSActivity) list.add((IRSActivity)node);
		for (IRSNode child : node.getChildren()) {
			findActivitiesDFS(child, list);
		}
	}
	
	// *** Methods
	
	public void verifyWorkflow() throws WorkflowStructureException {
		// TODO : verify workflow
	}
	
	@Override
	public Set<INode> getAllNodes() {
		Set<INode> nodes = new LinkedHashSet<INode>();
		for (INode node : getRootPlace().getVisibleNodes()) {
			nodes.add(node);
		}
		return nodes;// XXX : prawdopodobnie trzeba tu kopiować i wyrzucać zbędne łuki
	}
	/*
	@Override
	public Collection<Place> getPlaces() {
		Set<Place> nodes = new LinkedHashSet<Place>();
		for (INode node : getRootPlace().getExpandedNodes()) {
			if (node instanceof Place) {
				nodes.add((Place)node);
			}
		}
		return nodes;
	}

	@Override
	public Collection<Transition> getTransitions() {
		Set<Transition> nodes = new LinkedHashSet<Transition>();
		for (INode node : getRootPlace().getExpandedNodes()) {
			if (node instanceof Place) {
				nodes.add((Transition)node);
			}
		}
		return nodes;
	}
	*/
	
	protected void locateInOut() {
		boolean foundIn = false;
		boolean foundOut = false;
		setIn(null);
		setOut(null);
		
		for (IPlace place : getPlaces()) {
			if (place instanceof IRSPlace) {
				IRSPlace irsPlace = Converter.asIRSPlace(place);
				if (irsPlace.getIncomingArcs().size() == 0 &&
						irsPlace.isLeaf()) {
					if (foundIn) throw new InternalException(Cause.WORKFLOW_TOO_MANY_IN_PLACES);
					setIn(place);
					foundIn = true;
				}
			}
		}

		for (IPlace place : getPlaces()) {
			if (place instanceof IRSPlace) {
				IRSPlace irsPlace = Converter.asIRSPlace(place);
				if (irsPlace.getOutgoingArcs().size() == 0 &&
						irsPlace.isLeaf()) {
					if (foundOut) throw new InternalException(Cause.WORKFLOW_TOO_MANY_OUT_PLACES);
					setOut(place);
					foundOut = true;
				}
			}
		}
		
		if (!foundIn) throw new InternalException(Cause.WORKFLOW_NO_IN_PLACE);
		if (!foundOut) throw new InternalException(Cause.WORKFLOW_OUT_PLACE);
	}
	
	public Set<Role> getUsedRoles() {
		Set<Role> roles = new LinkedHashSet<Role>();
		for (IRSRequirement irsRequirement : getRequirements()) {
			for (Requirement requirement : irsRequirement.getRequirements()) {
				for (Role role : requirement.getRolesMap().keySet()) {
					if (!roles.contains(role)) roles.add(role);
				}
			}
		}
		return roles;
	}
	
}
